<?php
/**
 * Created by PhpStorm.
 * User: guokunsheng
 * Date: 2018/6/6
 * Time: 17:35
 */

namespace app\api\controller\v2;

use app\api\controller\Common;
use app\common\cachemodel\AppConfig;
use app\common\lib\exception\ApiException;
use app\common\lib\IAuth;
use app\common\lib\Aes;
use app\common\lib\Alidayu;
use app\common\lib\Curl;
use app\common\lib\Util;
use app\common\model\User;
use think\Cache;
use \think\Db;
use think\Log;

/**
 * @title 登录相关接口
 * @desc 账号密码登录 <br/>手机号验证码登录<br/>微信登录<br/>QQ登录<br/>微博登录<br/>
 * @version 2.0.0
 * @url /api/v2/login/...
 * @readme /doc/api/login/login.md
 */
class Login extends Common
{
    protected $restActionList = ['account', 'mobile', 'wechat', 'qq', 'weibo'];

    const WECHAT_ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token';
    const WECHAT_GET_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo';

    /**
     * @title 账号密码登录
     * @method POST
     * @return array
     */
    public function account()
    {
        if (!request()->isPost())
        {
            return show(config('code.error'), '未知操作', '', 403);
        }

        // 手机号是否存在
        if (empty($param['tel']))
        {
            return show(config('code.error'), '手机号错误');
        }

        // 生成token  和 token到期时间
        $token = IAuth::setAppLoginToken($param['tel']);
        $data = [
            'token' => $token,
            'token_expire' => strtotime("+" . config('app.login_time_out_day') . " days"),
        ];

        // 查询这个手机号是否存在
        $UserModel = new User();
        try
        {
            $user = $UserModel->field('forbidden,has_self_recode,password,uen')->where(['tel' => $param['tel']])->find();
            $UserModel->check_uen($user);
        }
        catch (\Exception $e)
        {
            $user = null;
            Log::write('账号密码登录时获取用户信息失败 ： ' . $e->getMessage(), 'error', true);
        }


        // 该用户是否正常  forbidden==1 禁止登录
        if ($user && $user->forbidden == 0)
        {
            // 密码登录时，进行密码验证
            if (!empty($param['password']))
            {
                // 判定用户的密码 和 $param['password'] 加密之后
                if (IAuth::setPassword($param['password']) != $user->password)
                {
                    return show(config('code.error'), '密码不正确');
                }
            }
            $id = $UserModel->save($data, ['tel' => $param['tel']]);

        } else
        {
            if (!empty($param['code']))
            {
                // 第一次登录 注册数据
//                $data['username'] = '手机用户-' . $param['tel'];
                $data['status'] = 1;
                $data['tel'] = $param['tel'];
                $data['channel'] = Util::getChannelId();
                $id = $UserModel->insertGetId($data);
            } else
            {
                return show(config('code.error'), '用户不存在');
            }
        }

        if ($id)
        {
            $result = [
                'access-token' => (new Aes())->encryptWithOpenssl($token . "||" . $id),
            ];
            return show(config('code.success'), 'ok', $result);
        } else
        {
            return show(config('code.error'), '登录失败');
        }

    }

    /**
     * 获取短信验证码
     * @return mixed
     * @throws ApiException
     */
    public function get_code()
    {
        $tel = input('tel/s');

        //手机号可否注册检查
        if (!config('apple_pay_goods.mobile_regist'))
        {
            $this->CheckUserTel($tel);
        } else
        {
            // 手机号是否存在
            if (!preg_match("/^1[3456789]{1}\d{9}$/", $tel))
            {
                Log::write('获取验证码手机号错误：' . $tel, 'error', true);
                throw new ApiException('手机号错误', 200, 0);
            }
        }

        $time = time();// 当前时间  有效时间暂定为十分钟 600s

        $expire = 600;

        $ip = $this->request->ip();

        try
        {
            $data = Db::table('app_tel_login')->where(['tel' => "$tel", 'expire_time' => ['>', $time]])->find();
        }
        catch (\Exception $e)
        {
            Log::write('登陆时，获取验证码失败 ： ' . $e->getMessage(), 'error', true);
        }
        // 十分钟内无已有验证码
        if (empty($data))
        {
            $code = mt_rand(100000, 999999);// 六位验证码

            $expire_time = $time + $expire;

            if (!$id = Db::table('app_tel_login')->insertGetId([
                'tel' => "$tel",
                'create_time' => $time,
                'expire_time' => $expire_time,
                'code' => $code,
            ]))
            {
                return show(config('code.error'), '发送失败');
            }
        } else
        {
            $code = $data['code'];
        }

        $alidayu = (new AppConfig())->configInfo('alidayu');
        $result = Alidayu::getInstance()->sendSms($tel, $code, $alidayu['login_code']);

        if (!$result)
        {
            return show(config('code.error'), '发送失败');
        } else
        {

            // 记录短信日志
            model('log')->save([
                'tel' => "$tel",
                'ip' => $ip,
                'create_time' => $time,
                'type' => 1,
            ]);

            return show(config('code.success'), '发送成功');
        }
    }

    /**
     * @title 手机验证码登录
     * @method POST
     * @return array
     * @throws ApiException
     */
    public function mobile()
    {
        if (!request()->isPost())
        {
            return show(config('code.error'), '未知操作');
        }

        $param = $this->request->only(['tel', 'code', 'password']);
        $time = $this->request->time();
        $ip = $this->request->ip();
        $appConfig = config('app');

        // 手机号不可注册
        $user = $this->CheckUserTel($param['tel']);
        // 短信验证码 或 密码是否存在
        if (empty($param['code']) && empty($param['password']))
        {
            //$param['code'] = Aes::decrypt($param['code']); // 1234
            return show(config('code.error'), '手机短信验证码或者密码不合法');
        }
        // 短信登录时，检测验证码是否正确
        if (!empty($param['code']))
        {
            if (!in_array($param['tel'], $appConfig['mobile'])
                || $param['code'] != substr($appConfig['code'] * (substr($param['tel'], 3, 6)), 0, 6))
            {
                //苹果和安卓应用商店审核账号，固定验证码为666666
                if (($param['tel'] == '13045794520' && $param['code'] == '666666')
                    || ($param['tel'] == '13261877781' && $param['code'] == '666666'))
                {

                } else
                {
                    //  validate 严格校验
                    $code = Alidayu::getInstance()->checkSmsIdentify($param['tel']);

                    if ($code != $param['code'])
                    {
                        return show(config('code.error'), '短信验证码错误');
                    }
                }
            }
        }
        // 生成token  和 token到期时间
        $token = IAuth::setAppLoginToken($param['tel']);

        $data = [
            'token' => $token,
            'token_expire' => strtotime("+" . $appConfig['login_time_out_day'] . " days"),
        ];
        $UserModel = new User();
        //判断用户是否绑定准了号
        if ($user)
        {
            $UserModel->check_uen($user);
        }

        $nickname = preg_replace('/(1[3456789]{1}[0-9])[0-9]{4}([0-9]{4})/i', '$1****$2', $param['tel']);

        // 该用户是否正常  forbidden==1 禁止登录
        if ($user && $user['forbidden'] == 0)
        {
            // 已过期重新生成access_token
            if ($time > $user['token_expire'] || !in_array($param['tel'], $appConfig['mobile']))
            {
                $UserModel->save($data, ['tel' => $param['tel']]);
            } else
            {
                $token = $user['token'];
            }
//            $UserModel->save($data, ['tel' => $param['tel']]);

            if (!$user['nickname'])
            {
                try
                {
                    Db::table('app_user')->where('tel', $param['tel'])
                        ->update(['nickname' => $nickname]);
                }
                catch (\Exception $e)
                {
                    Log::write('手机验证码登陆时初始化用户昵称失败' . $e->getMessage(), true);
                }
            }
        } elseif ($user && $user['forbidden'] == 1)
        {
            return show(config('code.error'), '该账号禁止登录');
        } //手机号不可注册
        elseif (!$user && !empty($param['code']) && !empty($param['tel']))
        {
            // 第一次登录 注册数据
            $user['uen'] = substr(md5($this->request->time(true) . mt_rand(0, 1000000) . uniqid()), 0, 12);
            $user['nickname'] = $nickname;
            $user['reg_time'] = $time;
            $user['reg_ip'] = $ip;
            $user['last_login'] = $time;
            $user['last_ip'] = $ip;
            $user['last_active'] = $time;
            $user['status'] = 1;
            $user['sex'] = 1;
            $user['tel'] = $param['tel'];
            $user['is_first_login'] = 1;
            $user['uc_id'] = md5($param['tel'] . $this->request->time(true));
            $user['token'] = $data['token'];
            $user['token_expire'] = $data['token_expire'];
            $user['has_self_recode'] = 0;
            $user['channel'] = Util::getChannelId();
            try
            {
                $user['id'] = $UserModel->insertGetId($user);
            }
            catch (\Exception $e)
            {
                return show(config('code.error'), '注册失败,请稍后重试');
            }

            try
            {
                $msg = Db::table('app_message_center')->field('id')->where('range', 0)->select();
            }
            catch (\Exception $e)
            {
                Log::write('获取系统消息失败' . $e->getMessage(), true);
            }
            foreach ($msg as $k => $v)
            {
                Db::table('app_user_msg')->insert(['uid' => $user['id'], 'mid' => $v['id']]);
            }

            // 用户星盘配置初始化  仅当初次登陆时初始化   改成异步操作
            $set = new AstroSetting();
            $set->init($user['id']);
        }

        if ($user)
        {
            $id = (int)($user['id']);
            // 账号模拟
            $simulation = $appConfig['simulation'];

            if ($id === $simulation['from'] && isset($simulation['to']) && $simulation['to'])
            {
                $id = $simulation['to'];
            }

            $result = [
                'id' => $id,
                'has_self_recode' => $user['has_self_recode'],
                'user_access_token' => (new Aes())->encryptWithOpenssl($token . "||" . $user['id']),
                'uc_id' => $user['uc_id'],
            ];
            // 记录登陆日志
            Db::table('app_login_log')->insertGetId([
                'uid' => $user['id'],
                'login_time' => $time,
                'login_type' => 4,
                'login_ip' => $ip,
            ]);
            return show(config('code.success'), 'ok', $result);
        }
        return show(config('code.error'), '登录失败');
    }

    /**
     * @title 微信登录
     * @method POST
     * @return array
     * @throws ApiException
     */
    public function wechat()
    {
        if (!request()->isPost())
        {
            return show(config('code.error'), '未知操作');
        }

        $param = input('param.');
        // 第三方微信登录
        if (!isset($param['code']) || !trim($param['code']))
        {
            if (!isset($param['userinfo']) || !$param['userinfo'])
            {
                return show(config('code.error'), 'userinfo参数不能为空');
            }

            $user_info = json_decode($param['userinfo'], true);

//            \think\Log::write(var_export($user_info, true), 'log', true);

            if (
                !isset($user_info['nickname'])
                || !isset($user_info['sex'])
                || !isset($user_info['city'])
                || !isset($user_info['province'])
                || !isset($user_info['country'])
                || !isset($user_info['headimgurl'])
                || !isset($user_info['openid'])
                || !isset($user_info['unionid'])
            )
            {
                return show(config('code.error'), '用户信息错误');
            }
            $access_token = [
                'access_token' => '',
                'refresh_token' => '',
                'expires_in' => 0,
                'scope' => '',
            ];
        } else
        {
            $access_token_url = self::WECHAT_ACCESS_TOKEN_URL . '?appid=' . config('wechat.wechat_appid') . '&secret=' . config('wechat.wechat_appsecret')
                . '&code=' . $param['code'] . '&grant_type=authorization_code';

            $access_token = Curl::get($access_token_url);

            $access_token = json_decode($access_token, true);

//            \think\Log::write('短信通知：'. var_export($access_token,true),'log',true);
//            halt($access_token);

            if (isset($access_token['errcode']))
            {
                return show(config('code.error'), $access_token['errcode'] . ':' . $access_token['errmsg'], []);
            }

            $userinfo_url = self::WECHAT_GET_USERINFO_URL . "?access_token={$access_token['access_token']}&openid={$access_token['openid']}&lang=zh_CN";

            $user_info = Curl::get($userinfo_url);

            $user_info = json_decode($user_info, true);

        }

        if (isset($user_info['errcode']))
        {
            return show(config('code.error'), $user_info['errcode'] . ':' . $user_info['errmsg']);
        }

        // 生成token  和 token到期时间
        $token = IAuth::setAppLoginToken($user_info['openid']);
        $data = [
            'token' => $token,
            'token_expire' => strtotime("+" . config('app.login_time_out_day') . " days"),
        ];

        $user = model('Wechat')
            ->alias('w')
            ->field('u.id,u.uen,u.nickname uname,w.nickname,u.avatar,u.forbidden,u.email,u.is_first_login,u.uc_id,u.sex,u.has_self_recode')
            ->join('app_user u', 'u.id=w.uid', 'INNER')
            ->where(['unionid' => $user_info['unionid']])->find();

        $UserModel = new User();

        $UserModel->check_uen($user);

        if (!$user['uname'])
        {
            $UserModel->where(['id' => $user['id']])->update(['nickname' => $user_info['nickname']]);
        }

        if (!$user['avatar'])
        {
            $UserModel->where(['id' => $user['id']])->update(['avatar' => $user_info['headimgurl']]);
        }

        $time = $this->request->time();

        $ip = $this->request->ip();
        // 第一次登录
        if (empty($user))
        {
            $avatarInfo = Util::wxImgUploadOss($user_info['headimgurl'], 'uploads/avatar');

            $avatar = $avatarInfo['success'] ? $avatarInfo['url'] : $user_info['headimgurl'];

            $user = [
                'uen' => substr(md5(microtime() . mt_rand(0, 1000000) . uniqid()), 0, 12),
                'nickname' => $user_info['nickname'],
                'avatar' => $avatar,
                'sex' => $user_info['sex'],
                'reg_time' => $time,
                'reg_ip' => $ip,
                'last_login' => $time,
                'last_ip' => $ip,
                'last_active' => $time,
                'is_first_login' => 1,// 首次登陆标记
                'uc_id' => md5($user_info['unionid'] . microtime()),
                'token' => $token,
                'token_expire' => strtotime("+" . config('app.login_time_out_day') . " days"),
                'channel' => Util::getChannelId()
            ];

            try
            {
                $user['id'] = $UserModel->insertGetId($user);
                $msg = Db::table('app_message_center')->field('id')->where('range', 0)->select();
                foreach ($msg as $k => $v)
                {
                    Db::table('app_user_msg')->insert(['uid' => $user['id'], 'mid' => $v['id']]);
                }

            }
            catch (\Exception $e)
            {
                return show(config('code.error'), $e->getMessage());
            }
            $wechat = [
                'uid' => $user['id'],
                'nickname' => $user_info['nickname'],
                'sex' => $user_info['sex'],
                'city' => $user_info['city'],
                'province' => $user_info['province'],
                'country' => $user_info['country'],
                'headimgurl' => $user_info['headimgurl'],
                'openid' => $user_info['openid'],
                'unionid' => $user_info['unionid'],
                'create_time' => $time,
                'update_time' => $time,
                'access_token' => $access_token['access_token'],
                'refresh_token' => $access_token['refresh_token'],
                'expires_in' => $access_token['expires_in'] + $time,
                'scope' => $access_token['scope'],
            ];
            // 用户星盘配置初始化  仅当初次登陆时初始化   改成异步操作
//            $set = new AstroSetting();
//            $set->init($user['id']);
            try
            {
                model('Wechat')->insertGetId($wechat);
            }
            catch (\Exception $e)
            {
                return show(config('code.error'), $e->getMessage());
            }
            $user['email'] = null;
            $user['forbidden'] = 0;
            $user['has_self_recode'] = 0;
            $user['channel'] = Util::getChannelId();
        } // 再次登录
        else
        {
            if ($user['forbidden'] == 1)
            {
                return show(config('code.error'), '该账号禁止登录');
            }

            if (empty($user['avatar']) || preg_match('/^http(s?):\/\/*/', $user['avatar']))
            {
                $avatarInfo = Util::wxImgUploadOss($user_info['headimgurl'], 'uploads/avatar');
                if ($avatarInfo['success'])
                {
                    $data['avatar'] = $avatarInfo['url'];
                    Cache::rm('userinfo:' . $user['id']);
                    // 更新档案信息
                    $recordModel = new \app\common\cachemodel\Recode();
                    $rid = $recordModel->where('uid', $user['id'])->where('isself', 1)->value('id');
                    $recordModel->UpdateData($user['id'], $rid, ['avatar' => $avatarInfo['url']]);
                }
            }

            try
            {
                $UserModel->save($data, ['id' => $user['id']]);
            }
            catch (\Exception $e)
            {
                return show(config('code.error'), $e->getMessage());
            }

        }

        // 记录登陆日志  改成异步操作
        Db::table('app_login_log')->insertGetId([
            'uid' => $user['id'],
            'login_time' => $time,
            'login_type' => 1,
            'login_ip' => $ip,
        ]);

        $id = (int)($user['id']);
        // 账号模拟
        $simulation = config('app.simulation');

        if ($id === $simulation['from'] && isset($simulation['to']) && $simulation['to'])
        {
            $id = $simulation['to'];
        }

        $result = [
            'id' => $id,
            'has_self_recode' => $user['has_self_recode'],
            'user_access_token' => (new Aes())->encryptWithOpenssl($token . "||" . $user['id']),
            'uc_id' => $user['uc_id'],
        ];

        return show(config('code.success'), '登录成功', $result);
    }

    /**
     * @title QQ登录
     * @method POST
     * @return array
     */
    public function qq()
    {
        return show(0, '暂未开放', [], 404);
    }

    /**
     * @title 微博登录
     * @method POST
     * @return array
     */
    public function weibo()
    {
        return show(0, '暂未开放', [], 404);
    }

    /**
     * 检测手机号格式，不可注册
     * @param $tel
     * @return array|\PDOStatement|string|\think\Model
     * @throws ApiException
     */
    public function CheckUserTel($tel)
    {
        // 手机号是否存在
        if (!preg_match("/^1[3456789]{1}\d{9}$/", $tel))
        {
            Log::write('手机号错误：' . $tel, 'error', true);
            throw new ApiException('手机号错误', 200, 0);
        }
        //判断手机号是否注册过
        try
        {
            $user = (new \app\common\cachemodel\User())->field('id,forbidden,has_self_recode,password,nickname,uc_id,avatar,token_expire,token,uen')->where(['tel' => $tel])->findOrFail()->toArray();
        }
        catch (\Exception $e)
        {
            if (config('apple_pay_goods.mobile_regist'))
            {
                $user = [];
            } else
            {
                $user = [];
                // throw new ApiException('手机号注册失败，请下载最新版本', 200, 0);
            }
        }
        return $user;
    }

    /**
     * 参数规则
     * @name 字段名称
     * @type 类型
     * @require 是否必须
     * @default 默认值
     * @desc 说明
     * @range 范围
     * @return array
     */
    public static function getRules()
    {
        $rules = [
            'account' => [
                'tel' => ['name' => 'tel', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '手机号码', 'range' => '11位手机号',],
                'code' => ['name' => 'password', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '密码', 'range' => '',],
            ],
            'mobile' => [
                'tel' => ['name' => 'tel', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '手机号码', 'range' => '11位手机号',],
                'code' => ['name' => 'code', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '短信验证码', 'range' => '6位',],
            ],
            'wechat' => [
                'code' => ['name' => 'code', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '微信授权码', 'range' => '',],
            ],
            'qq' => [
                'code' => ['name' => 'code', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => 'QQ授权码', 'range' => '',],
            ],
            'weibo' => [
                'code' => ['name' => 'code', 'type' => 'string', 'require' => 'true', 'default' => '无', 'desc' => '微博授权码', 'range' => '',],
            ],
        ];
        return $rules;
    }
}
